/*
 * Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
 *
 * Licensed under the Mulan PSL v2.
 * You can use this file according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of the License at
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
 * FOR MORE DETAILS.
 */

#pragma once

#include "Utility/Logging/SGLogger.h"

#include "DBManager/Common/TemplateFunction.h"

#define SERVICE_REG_IMPL1(ImplName, RepositoryClassName)                                                                   \
    template <>                                                                                                            \
    std::shared_ptr<I##ImplName> DBServiceFactory::get ()                                                                  \
    {                                                                                                                      \
        const size_t typeID = typeid (I##ImplName).hash_code ();                                                           \
        auto         iter   = m_ServiceRep.find (typeID);                                                                  \
        if (iter == m_ServiceRep.end ())                                                                                   \
        {                                                                                                                  \
            const std::shared_ptr<RepositoryClassName> RepSp = m_Database->GetRepository<RepositoryClassName> ();          \
            m_ServiceRep[typeID] =                                                                                         \
                std::shared_ptr<void> (new ImplName (*RepSp.get ()), [] (void* p) { delete static_cast<ImplName*> (p); }); \
        }                                                                                                                  \
        return std::static_pointer_cast<I##ImplName> (m_ServiceRep[typeID]);                                               \
    }

#define SERVICE_REG_WITH_DBM_IMPL(ImplName, RepositoryClassName)                                                                 \
    template <>                                                                                                                  \
    std::shared_ptr<I##ImplName> DBServiceFactory::get ()                                                                        \
    {                                                                                                                            \
        const size_t typeID = typeid (I##ImplName).hash_code ();                                                                 \
        auto         iter   = m_ServiceRep.find (typeID);                                                                        \
        if (iter == m_ServiceRep.end ())                                                                                         \
        {                                                                                                                        \
            const std::shared_ptr<RepositoryClassName> RepSp = m_Database->GetRepository<RepositoryClassName> ();                \
            m_ServiceRep[typeID] =                                                                                               \
                std::shared_ptr<void> (new ImplName (this, *RepSp.get ()), [] (void* p) { delete static_cast<ImplName*> (p); }); \
        }                                                                                                                        \
        return std::static_pointer_cast<I##ImplName> (m_ServiceRep[typeID]);                                                     \
    }

#define SERVICE_REG_FOR_SUMMARY(ImplName, RepositoryClassName)                                                                   \
    template <>                                                                                                                  \
    std::shared_ptr<I##ImplName> DBServiceFactory::get ()                                                                        \
    {                                                                                                                            \
        const size_t typeID = typeid (I##ImplName).hash_code ();                                                                 \
        auto         iter   = m_ServiceRep.find (typeID);                                                                        \
        if (iter == m_ServiceRep.end ())                                                                                         \
        {                                                                                                                        \
            ImplName::CreateSummary (this);                                                                                      \
            const std::shared_ptr<RepositoryClassName> RepSp = m_Database->GetRepository<RepositoryClassName> ();                \
            m_ServiceRep[typeID] =                                                                                               \
                std::shared_ptr<void> (new ImplName (this, *RepSp.get ()), [] (void* p) { delete static_cast<ImplName*> (p); }); \
        }                                                                                                                        \
        return std::static_pointer_cast<I##ImplName> (m_ServiceRep[typeID]);                                                     \
    }

#define SERVICE_REG_IMPL2(ImplName, RepositoryClassName1, RepositoryClassName2)                                                               \
    template <>                                                                                                                               \
    std::shared_ptr<I##ImplName> DBServiceFactory::get ()                                                                                     \
    {                                                                                                                                         \
        const size_t typeID = typeid (I##ImplName).hash_code ();                                                                              \
        auto         iter   = m_ServiceRep.find (typeID);                                                                                     \
        if (iter == m_ServiceRep.end ())                                                                                                      \
        {                                                                                                                                     \
            const std::shared_ptr<RepositoryClassName1> RepSp1 = m_Database->GetRepository<RepositoryClassName1> ();                          \
            const std::shared_ptr<RepositoryClassName2> RepSp2 = m_Database->GetRepository<RepositoryClassName2> ();                          \
            m_ServiceRep[typeID]                               = std::shared_ptr<void> (new ImplName (*RepSp1.get (), *RepSp2.get ()),        \
                                                          [] (void* p) { delete static_cast<ImplName*> (p); }); \
        }                                                                                                                                     \
        return std::static_pointer_cast<I##ImplName> (m_ServiceRep[typeID]);                                                                  \
    }

#define SERVICE_REG_RESULT(ImplName)                                                                               \
    namespace                                                                                                      \
    {                                                                                                              \
        bool ImplName##SummaryReg = IDatabaseHub::ConnectCopyDataRes (SG::DBManager::CopyDataResult<I##ImplName>); \
    }

#define SERVICE_REG_CORD_SUMMARY(ImplName)                                                                                   \
    namespace                                                                                                                \
    {                                                                                                                        \
        void Connect##ImplName##Function (DBServiceFactory* const dbServiceFactory)                                          \
        {                                                                                                                    \
            auto service = dbServiceFactory->get<I##ImplName> ();                                                            \
            auto type    = IRepos::GetType ();                                                                               \
                                                                                                                             \
            dbServiceFactory->GetDatabase ()->GetHub ()->ConnectGetAllCoord ([service] { return service->GetAllCoord (); }); \
                                                                                                                             \
            dbServiceFactory->GetDatabase ()->GetHub ()->ConnectCopyData (typeid (decltype (type)).hash_code () + type,      \
                                                                          SG::DBManager::CopyDataUnique<I##ImplName>);       \
        }                                                                                                                    \
        bool ImplName##SummaryReg = CORDSummaryService::RegisterService (Connect##ImplName##Function);                       \
    }

#define SERVICE_REG_SET_SUMMARY(ImplName, SummaryService)                                                              \
    namespace                                                                                                          \
    {                                                                                                                  \
        void Connect##ImplName##Function (DBServiceFactory* const dbServiceFactory)                                    \
        {                                                                                                              \
            auto service = dbServiceFactory->get<I##ImplName> ();                                                      \
            dbServiceFactory->GetDatabase ()->GetHub ()->ConnectCopyData (                                             \
                typeid (decltype (IRepos::GetType ())).hash_code () + IRepos::GetType (),                              \
                [service] (DBServiceFactory* const                    src,                                             \
                           DBServiceFactory* const                    dest,                                            \
                           std::vector<SG::DataStructure::Common::Id> ids) -> SG::DataStructure::Common::Status {      \
                    auto serviceSrc  = src->get<I##ImplName> ();                                                       \
                    auto serviceDest = dest->get<I##ImplName> ();                                                      \
                                                                                                                       \
                    std::vector<I##ImplName::ServiceDataType> entities;                                                \
                    std::vector<I##ImplName::ServiceDataType> temp;                                                    \
                                                                                                                       \
                    for (size_t i = 0; i < ids.size (); i++)                                                           \
                    {                                                                                                  \
                        if (SG::DataStructure::Common::Status::STATUS_Success == serviceSrc->FindBySId (ids[i], temp)) \
                        {                                                                                              \
                            entities.insert (entities.end (), temp.begin (), temp.end ());                             \
                        }                                                                                              \
                    }                                                                                                  \
                                                                                                                       \
                    return serviceDest->MAdd (entities);                                                               \
                });                                                                                                    \
        }                                                                                                              \
        bool ImplName##SummaryReg = SummaryService::RegisterService (Connect##ImplName##Function);                     \
    }

#define SERVICE_REG_SET_ISONE_SUMMARY(ImplName, SummaryService)                                                   \
    namespace                                                                                                     \
    {                                                                                                             \
        void Connect##ImplName##Function (DBServiceFactory* const dbServiceFactory)                               \
        {                                                                                                         \
            auto service = dbServiceFactory->get<I##ImplName> ();                                                 \
            dbServiceFactory->GetDatabase ()->GetHub ()->ConnectCopyData (                                        \
                typeid (decltype (IRepos::GetType ())).hash_code () + IRepos::GetType (),                         \
                [service] (DBServiceFactory* const                    src,                                        \
                           DBServiceFactory* const                    dest,                                       \
                           std::vector<SG::DataStructure::Common::Id> ids) -> SG::DataStructure::Common::Status { \
                    auto serviceSrc  = src->get<I##ImplName> ();                                                  \
                    auto serviceDest = dest->get<I##ImplName> ();                                                 \
                                                                                                                  \
                    std::vector<I##ImplName::ServiceDataType> entities (ids.size ());                             \
                                                                                                                  \
                    for (std::size_t i = 0; i < ids.size (); ++i)                                                 \
                    {                                                                                             \
                        serviceSrc->FindBySId (ids[i], entities[i]);                                              \
                    }                                                                                             \
                                                                                                                  \
                    serviceDest->MAdd (entities);                                                                 \
                    return SG::DataStructure::Common::Status::STATUS_Success;                                     \
                });                                                                                               \
        }                                                                                                         \
        bool ImplName##SummaryReg = SummaryService::RegisterService (Connect##ImplName##Function);                \
    }

#define SERVICE_REG_SUMMARY(ImplName, SummaryService)                                                                   \
    namespace                                                                                                           \
    {                                                                                                                   \
        void Connect##ImplName##Function (DBServiceFactory* const dbServiceFactory)                                     \
        {                                                                                                               \
            auto service = dbServiceFactory->get<I##ImplName> ();                                                       \
            auto type    = IRepos::GetType ();                                                                          \
                                                                                                                        \
            dbServiceFactory->GetDatabase ()->GetHub ()->ConnectCopyData (typeid (decltype (type)).hash_code () + type, \
                                                                          SG::DBManager::CopyDataUnique<I##ImplName>);  \
        }                                                                                                               \
        bool ImplName##SummaryReg = SummaryService::RegisterService (Connect##ImplName##Function);                      \
    }

#define FUNCTION_Add(DataType)                                                      \
    SG::DataStructure::Common::Status Add (DataType& entity) override               \
    {                                                                               \
        return m_repository.Add (entity);                                           \
    }                                                                               \
    SG::DataStructure::Common::Status MAdd (std::vector<DataType>& entity) override \
    {                                                                               \
        return m_repository.Add (entity);                                           \
    }

#define FUNCTION_AddBatch(DataType)

#define FUNCTION_Size()              \
    std::size_t Size () override     \
    {                                \
        return m_repository.Size (); \
    }

#define FUNCTION_GetAllId()                                         \
    std::vector<SG::DataStructure::Common::Id> GetAllId () override \
    {                                                               \
        return m_repository.GetAllId ();                            \
    }

#define FUNCTION_FindByIndex(DataType)                                                                \
    SG::DataStructure::Common::Status FindByIndex (std::size_t index, DataType& _OUT entity) override \
    {                                                                                                 \
        return m_repository.FindByIndex (index, entity);                                              \
    }

#define FUNCTION_FindById(DataType)                                                                               \
    SG::DataStructure::Common::Status FindById (SG::DataStructure::Common::Id id, DataType& _OUT entity) override \
    {                                                                                                             \
        return m_repository.FindById (id, entity);                                                                \
    }

#define FindBySIdFront(DataType)                                                                               \
    SG::DataStructure::Common::Status FindBySId (SG::DataStructure::Common::Id sId, DataType& entity) override \
    {                                                                                                          \
        std::vector<DataType> arrs;                                                                            \
        if (m_repository.FindBySId (sId, arrs) == SG::DataStructure::Common::Status::STATUS_Success)           \
        {                                                                                                      \
            entity = arrs.front ();                                                                            \
            return SG::DataStructure::Common::Status::STATUS_Success;                                          \
        }                                                                                                      \
        return SG::DataStructure::Common::Status::STATUS_Failure;                                              \
    }

#define FUNCTION_GetAllData(DataType)            \
    std::vector<DataType> GetAllData () override \
    {                                            \
        return m_repository.GetAllData ();       \
    }

#define FUNCTION_Count()                                          \
    std::size_t Count (SG::DataStructure::Common::Id id) override \
    {                                                             \
        return m_repository.Count (id);                           \
    }

#define FUNCTION_FindBySId(DataType)                                                                                             \
    ::SG::DataStructure::Common::Status FindBySId (SG::DataStructure::Common::Id sId, std::vector<DataType>& _OUT arrs) override \
    {                                                                                                                            \
        return m_repository.FindBySId (sId, arrs);                                                                               \
    }

#define FUNCTION_FindBySIdIsOne(DataType)                                                                          \
    ::SG::DataStructure::Common::Status FindBySId (SG::DataStructure::Common::Id sId, DataType& _OUT res) override \
    {                                                                                                              \
        std::vector<DataType>               data;                                                                  \
        ::SG::DataStructure::Common::Status resStatus = m_repository.FindBySId (sId, data);                        \
        if (data.size () > 0)                                                                                      \
            res = data[0];                                                                                         \
        return resStatus;                                                                                          \
    }

#define FUNCTION_POST_Add(DataType)                                                                                                  \
    SG::DataStructure::Common::Status Add (SG::DBManager::Post::DomainRequest request, std::vector<DataType>& data) override         \
    {                                                                                                                                \
        SG::DataStructure::Post::ResultDomains domain;                                                                               \
                                                                                                                                     \
        auto domainsService = this->m_dbServiceFactory->get<IResultDomainsService> ();                                               \
        if (domainsService->Find (request, domain) == SG::DataStructure::Common::Status::STATUS_Failure)                             \
        {                                                                                                                            \
            auto id = domainsService->AddFromRequest (request);                                                                      \
            if (id != 0)                                                                                                             \
                return m_repository.Add (id, data);                                                                                  \
            else                                                                                                                     \
                return SG::DataStructure::Common::Status::STATUS_Failure;                                                            \
        }                                                                                                                            \
                                                                                                                                     \
        return m_repository.Add (domain.m_id, data);                                                                                 \
    }                                                                                                                                \
                                                                                                                                     \
    SG::DataStructure::Common::Status AddBySubcaseId (SG::DataStructure::Common::Id subcaseId, std::vector<DataType>& data) override \
    {                                                                                                                                \
        return Add (SG::DBManager::Post::DomainRequest (subcaseId), data);                                                           \
    }                                                                                                                                \
    SG::DataStructure::Common::Status AddByDomainId (SG::DataStructure::Common::Id domainid, std::vector<DataType>& data) override   \
    {                                                                                                                                \
        return m_repository.Add (domainid, data);                                                                                    \
    }

#define FUNCTION_POST_Size()                                                          \
    std::size_t Size (SG::DBManager::Post::DomainRequest request) override            \
    {                                                                                 \
        SG::DataStructure::Post::ResultDomains domain;                                \
                                                                                      \
        if (m_dbServiceFactory->get<IResultDomainsService> ()->Find (request, domain) \
            == SG::DataStructure::Common::Status::STATUS_Failure)                     \
        {                                                                             \
            return 0;                                                                 \
        }                                                                             \
        return m_repository.Size (domain.m_id);                                       \
    }

#define FUNCTION_POST_Find(DataType)                                                                                                 \
    SG::DataStructure::Common::Status Find (SG::DBManager::Post::DomainRequest request, std::vector<DataType>& data)                 \
    {                                                                                                                                \
        SG::DataStructure::Post::ResultDomains domain;                                                                               \
                                                                                                                                     \
        if (m_dbServiceFactory->get<IResultDomainsService> ()->Find (request, domain)                                                \
            == SG::DataStructure::Common::Status::STATUS_Failure)                                                                    \
        {                                                                                                                            \
            return SG::DataStructure::Common::Status::STATUS_Failure;                                                                \
        }                                                                                                                            \
        return m_repository.FindByDomainId (domain.m_id, data);                                                                      \
    }                                                                                                                                \
                                                                                                                                     \
    SG::DataStructure::Common::Status Find (                                                                                         \
        SG::DBManager::Post::DomainRequest request, std::set<SG::DataStructure::Common::Id> eIdSet, std::vector<DataType>& entities) \
    {                                                                                                                                \
        SG::DataStructure::Post::ResultDomains domain;                                                                               \
                                                                                                                                     \
        if (m_dbServiceFactory->get<IResultDomainsService> ()->Find (request, domain)                                                \
            == SG::DataStructure::Common::Status::STATUS_Failure)                                                                    \
        {                                                                                                                            \
            return SG::DataStructure::Common::Status::STATUS_Failure;                                                                \
        }                                                                                                                            \
        return m_repository.FindByDomainId (domain.m_id, eIdSet, entities);                                                          \
    }                                                                                                                                \
                                                                                                                                     \
    SG::DataStructure::Common::Status Find (SG::DataStructure::Common::Id domainId, std::vector<DataType>& data)                     \
    {                                                                                                                                \
        return m_repository.FindByDomainId (domainId, data);                                                                         \
    }

#define FUNCTION_GetConfrigure(DataType) \
    DataType Get ()                      \
    {                                    \
        return m_repository.Get ();      \
    }

#define FUNCTION_SetConfrigure(DataType) \
    void Set (const DataType& data)      \
    {                                    \
        m_repository.Set (data);         \
    }

#define FUNCTION_DEFAULT(DataType)   \
    FUNCTION_Add (DataType);         \
    FUNCTION_FindByIndex (DataType); \
    FUNCTION_FindById (DataType);    \
    FUNCTION_Size ();                \
    FUNCTION_GetAllId ();            \
    FUNCTION_Count ()

#define FUNCTION_SET(DataType)     \
    FUNCTION_Add (DataType);       \
    FUNCTION_AddBatch (DataType);  \
    FUNCTION_FindBySId (DataType); \
    FUNCTION_Size ()

#define FUNCTION_POST_DEFAULT(DataType) \
    FUNCTION_POST_Add (DataType);       \
    FUNCTION_POST_Find (DataType);      \
    FUNCTION_POST_Size ();

#define FUNCTION_CONFIGURE_GETSET(DataType) FUNCTION_GetConfrigure (DataType) FUNCTION_SetConfrigure (DataType)